// 指定GLSL版本为4.40
#version 440

// 输入：纹理坐标，location=0表示这是第一个输入变量
layout(location = 0) in vec2 qt_TexCoord0;

// 输出：片段颜色，location=0表示这是第一个输出变量
layout(location = 0) out vec4 FragColor;

// 统一缓冲区（Uniform Buffer Object）定义，包含所有需要的参数
layout(std140, binding = 0) uniform buf {
    mat4 qt_Matrix;     // 变换矩阵
    float qt_Opacity;   // 不透明度
    vec2 size;         // 尺寸
    vec4 color;        // 主颜色
    vec4 dark;         // 暗部颜色
    vec4 light;        // 亮部颜色
    vec4 radius;       // 四个角的圆角半径
    vec4 attrs;        // 特殊属性(x:扩散, y:角度, z:内边距, w:混合)
    float style;       // 样式
};

// 辅助函数：根据style参数进行平滑过渡
float ss(in vec2 arg, in float x) {
    // style 跟 qml 中的 inward 变量有关; 内陷的NuemEffect style都为1;
    vec2 tmp = mix(arg.yx, arg.xy, style);  // 根据style混合参数
    return smoothstep(tmp.x, tmp.y, x);     // 返回平滑过渡值
}

void main() {
    // 计算基本参数
    float _min = min(size.x, size.y),                    // 获取最小边长
          spread = clamp(attrs.x, 1.0 / _min, 1.0),      // 限制扩散范围
          angle = attrs.y,                               // 光影角度
          padding = attrs.z,                             // 内边距
          blend = attrs.w,                               // 混合程度
          // 根据外部QML的 inward; style = inward;  当inward = true; _innerpad = attrs.x / 2.0; attrs.x 即spread;
          _innerpad = mix(0.0, attrs.x / 2.0, style);    // 内部填充

    // 计算相对尺寸
    vec2 _rsize = .5 * (size / _min - 1.0);

    // 将纹理坐标归一化到合适的范围(图形中心移动至原点) 从 [0,1] -> [-0.5, 0.5] 
    // 然后使用 size/_min 保持宽高比 确保在不同尺寸的控件下效果一致
    /*
    详解:
        坐标归一化：短边始终被缩放为1单位长度
        保持比例：长边按照相同比例缩放
        效果一致性：
            圆角在各个角落保持相同的曲率
            阴影效果在不同尺寸下保持视觉一致性
            光影混合的过渡区域比例保持不变

        归一化举例:
        [200, 100] 的矩形; min = 100; 
        vec2_size / vec2_min -> [200/100, 100/100] = [2, 1]
        _rsize = 0.5 * ([2, 1] - [1, 1]) = [0.5, 0]
        所以在计算rdist时, 需要减去_rsize做修正， 因为是使用正方形的SDF计算方法 abs(uv) - c; 直接减去一个常数; 
        float rdist = length( (max(abs(uv) - _rsize) - 0.5 + rad + spread / 2.0 + padding / 2.0, 0.0)) - rad;
        除以2 的原因就是因为 uv 是 [-0.5, 0.5] 的范围; 且只处理第一象限; 也就是一个矩形的半宽半高
    */

    vec2 uv = (qt_TexCoord0 - 0.5) * size / _min;
    
    // 根据UV坐标选择适当的圆角半径 mix(a, b, c) -> b*c + a*(1-c);
    // step当 uv.x < 1.0 时 返回 0.0，否则返回 1.0;  
    // 四角圆角半径（x:左上 y:右上 z:右下 w:左下）
    vec2 temp = mix(radius.xw, radius.yz, step(uv.x, 1.0));         // 判断左右 xw 表示要渲染左边的圆角半径，yz表示要渲染右边的圆角半径
    float rad = mix(temp.x, temp.y, step(uv.y, 1.0));               // 判断上下

    // 计算颜色混合值，基于角度和位置 -> 基于角度的颜色混合系数
    // blend 混合;  此处用于控制边缘之外渐变的软硬程度;  
    // 角度angle是固定的 uv是当前像素点的坐标 
    float _cblend = dot(uv, normalize( vec2(sin(angle), cos(angle)) )) / blend + 0.5;
    
    // 确保扩散值在有效范围内
    spread = clamp(spread, 1.0 / _min, 1.0);
    // 计算实际使用的圆角半径
    rad = min((1.0 - spread - padding) / 2.0, rad);

    // 计算到边缘的距离
    /*
        float sdBox(vec2 p, vec2 b) {
            vec2 d = abs(p) - b;
            return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
        sdBox函数是处理矩形的 可以是正方形也可以是其他矩形
        但是rdist中 直接 -0.5 是直接处理的正方形 所以如果有控件是长方形 需要加上 _rsize 做非正方形补偿
        整个正方形 [-0.5, 0.5]   sdBox函数的 vec2 b; 是矩形的一半宽高
    */

    // spread 和 padding /2 的原因是 Shader处理一个像素点时(uv), 
    // 将其移动到了[-0.5, 0.5]的范围, 并且只处理了第一象限, 也就是一个矩形的半宽半高 
    //  _innerpad? -> 如果是inward = true; NeumEffect 内部的 NeumEffect, _innerpad = spread / 2.0; 需要减去spread, spread参数只有外部的 NeumEffect有
    // 在 QML中, NeumEffect作为控件的Background, 占据了整个控件, 但是 pad 让background(的按钮部分,非环绕的光效部分)缩小了一部分(pad越大按钮部分越小), 也就是说, NeumEffect的内部NeumEffect的半径是不变的, 但是SDF的计算需要 + pad/2
    // 整个NeumEffect由 光影效果部分(spread) + 按钮部分 + pad 组成
    // 但是在计算SDF时, 边界的定义是按钮的主体部分, 不包含 spread 和 pad 所以需要加上 spread / 2.0 + padding / 2.0
    float rdist = length(max(abs(uv) - 0.5 + rad + spread / 2.0 + padding / 2.0 - _rsize, 0.0)) - rad;
    
    /*
    length 实现
    float length(vec2 v) {
        return sqrt(v.x * v.x + v.y * v.y);
    }
    smoothstep 函数的实现: 
    float smoothstep(float edge0, float edge1, float x) {
        float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
        return t * t * (3.0 - 2.0 * t);
    }
    smoothstep 函数的效果是在 edge0 处为 0，在 edge1 处为 1，两者之间平滑过渡
    */

    // 计算最终颜色
    // 一个Button的Background包含两个NeumEffect, 内部的 spread = 4 * control.checked; 
    // 且内部的NeumEffect光效角度和外部的相差180度
    // 说明按钮按下时(checkable按钮), 内部的NeumEffect由于光效的变化会显示出一种凹陷的效果
    // 然后这个 4/_min; 含义是 QML中的 4*control.checked; 即4个像素点, 使用短边归一化到uv坐标系范围中 4/_min
    vec4 glow = mix(
        mix(dark, light, clamp(_cblend, 0.0, 1.0)),  // 混合暗部和亮部
        color,                                        // 与主颜色混合
        ss(vec2(0.0, 4.0 / _min), rdist - _innerpad) // 基于距离的平滑过渡
    );

    // 设置最终输出颜色，包含透明度
    FragColor = glow * ss(vec2(0.0, spread), rdist + _innerpad / 2.0) * qt_Opacity;
}